package com.flong.controller;

import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSON;
import com.flong.entity.User;
import com.flong.mapper.IUserMapper;
import com.flong.service.IRedisService;
import com.flong.service.IUserService;
import com.flong.utils.DateUtil;
import com.flong.utils.ObjectUtil;
import com.flong.utils.annotation.Log;
import com.flong.utils.annotation.NeedLogin;
import com.flong.vo.ResultVo;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;

/***
 * @Description	UserController
 * @ClassName	UserController
 * @Date		2018年2月27日 上午10:09:25
 * @Author		liangjl
 * @Copyright (c) All Rights Reserved, 2018.
 */
@Controller
@SuppressWarnings("all")
@Api("用户模块API")
public class UserController {
	
	private final String USER_REDIS_KEY = "QUERY_USER_LIST_REDIS_KEY"; 
	private final String USER_REDIS_BACKUP_KEY = "USER_REDIS_BACKUP_KEY"; 
	private final static String CURRENT_USER  = "CURRENT_USER";
	private final static Logger logger = LoggerFactory.getLogger(UserController.class);
	
	@Autowired private IUserService userService;
	@Autowired private IRedisService redisService;
	@Autowired private IUserMapper userMapper;
	
	
	//创建公平锁
	private Lock rock = new ReentrantLock(true);
	
	@ResponseBody
	@RequestMapping("/delById")
	public String delById(Integer id) {
	 
		userMapper.deleteById(id);
		logger.info("删除成功！");
		return "Success";
	}
	
	
	/**
	 * @api {post} /login 用户登陆
	 * @apiName login
	 * @apiGroup User
	 *
	 * @apiParam {Integer} 	userName  用户名称.
	 * @apiParam {String} 	passWord  用户密码.
	 * 
	 * @apiSuccess {String} code 返回编码.
	 * @apiSuccess {String} msg  返回信息 .
	 *
	 * @apiSuccessExample Success-Response:
	 *     HTTP/1.1 200 OK
	 *     {
	 *       "code": "0",
	 *       "msg": "登录成功"
	 *     }
	 *
	 * @apiError 错误信息   系统找不到相应的信息.
	 *
	 * @apiErrorExample Error-Response:
	 *     HTTP/1.1 404 Not Found
	 *     {
	 *       "error": "系统找不到相应的信息"
	 *     }
	 */
	@ResponseBody
	@RequestMapping("/login")
	public ResultVo login(String userName,HttpServletRequest request) {
	 
		User user = userMapper.findByUserName(userName);
		if(!ObjectUtil.isEmpty(user)) {
			//把对象存入session
			request.getSession().setAttribute(CURRENT_USER, user);
			
			logger.info("登录成功！"+JSON.toJSONString(user));
			return new ResultVo(0,user);
		}else {
			return new ResultVo(1,"找不到用户信息");
		}
	}

	
	
	/**
	 * @Description 加上NeedLogin此注解必须要登录才可以访问
	 * @see UserController#login(String, HttpServletRequest)
	 * 访问：http://localhost:8080/login?userName=flong -->>再访问:http://localhost:8080/permission
	 * @Log -->此注解记录一下行为操作日志信息.加上表示记录,不加上表示不记录信息.
	 * 
	 * @Author		liangjl
	 * @Date		2018年2月23日 下午6:03:03
	 * @param request
	 * @return 参数
	 * @return ResultVo 返回类型 
	 * @throws
	 */
	@Log("授权登录日志")
	@NeedLogin
	@ResponseBody
	@RequestMapping("/permission")
	public ResultVo permission(HttpServletRequest request) {
		/*User user = WebUtils.getSessionCurrentUser(request);
		if(ObjectUtil.isNotEmpty(user)) {
			return new ResultVo(0,"Success");
		}else {
			return new ResultVo(1,"User Not Login");
		}*/
		return new ResultVo(0,"Success");
	}

	    
	

	@ApiOperation(value="查询用户信息",notes="获取用户信息")
	@ApiImplicitParams({
         @ApiImplicitParam(paramType="header",name="id",dataType="String",required=true,value="用户Id",defaultValue="1")
	})
    @ApiResponses({
        @ApiResponse(code=400,message="请求参数没填好"),
        @ApiResponse(code=404,message="请求路径没有或页面跳转路径不对")
    })
	@ResponseBody
	@GetMapping
	@RequestMapping("/queryUserInfo")
	public List<User> queryUserInfo(@RequestHeader("id")Integer id) {
		
		//1-->测试不使用缓存的查询
		//return nonUseRedis(id);
		
		//2-->测试使用redis缓存,但没线程synchronized重量级锁)安全的并发查询
		//return nonUseThreadButRedis(id);
	
		//3-->使用rock并发和redis进行处理缓存
	   return nonUseRockButRedis(id);
	   
	}

	/**
	 * @Description 没使用线程安全查询，但是使用了redis缓存,1000个并发的时候运行一段时间之后还是会挂好几个，虽然减少部分错误.
	 *              在方法加了synchronized虽然能解决部分的并发请求，但是超过1000以上一直卡死不动.完全死锁不释放.
	 * @Author		liangjl
	 * @Date		2018年2月3日 下午2:46:14
	 * @param id
	 * @return 参数
	 * @return List<User> 返回类型 
	 * @throws
	 */
	private  List<User> nonUseRockButRedis(Integer id) {
		List<User> retDataList = null;
		rock.lock();
		//如果请求拿到cup分配的线程锁就往下走.
		if(rock.tryLock()) {
			try {
				Date startDate = new Date() ;//开始时间
				String startDateStr  = DateUtil.formatDate(startDate, "yyyy-MM-dd HH:mm:ss");
				//从缓存获取数据
				retDataList = (List<User>)redisService.get(USER_REDIS_KEY+"_"+id);
				
				//判断缓存是否等于空
				if(ObjectUtil.isEmpty(retDataList)) {
					//查数据库
					retDataList = userService.getUserList(id);
					Date endDate = new Date() ;//结束时间
					redisService.set(USER_REDIS_KEY+"_"+id, retDataList,20L);
					
					//如果数据库的值为空就把它设置一个过期时间
					if(ObjectUtil.isEmpty(retDataList)) {
						redisService.set(USER_REDIS_KEY+"_"+id, retDataList,20L);
					}else {
						//放入备份缓存
						redisService.set(USER_REDIS_BACKUP_KEY+"_"+id, retDataList,20L);
					}
					String endDateStr= DateUtil.formatDate(endDate, "yyyy-MM-dd HH:mm:ss");
					logger.info("数据库-->执行情况-->>【开始时间为："+startDateStr+"\t,结束时间为："+endDateStr+
							"\t,执行秒数："+DateUtil.getBetweeDay(endDate, startDate)+"】");
					
					return retDataList;
				}else {
					Date endDate = new Date() ;//结束时间
					String endDateStr= DateUtil.formatDate(endDate, "yyyy-MM-dd HH:mm:ss");
					logger.info("缓存-->执行情况-->>【开始时间为："+startDateStr+"\t,结束时间为："+endDateStr+
							"\t,执行秒数："+DateUtil.getBetweeDay(endDate, startDate)+"】");
					
					return retDataList;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}finally {
				//手动释放锁
				rock.unlock();
			}
		}else {
			//从备份缓存获取springboot初始化.
			retDataList = (List<User>)redisService.get(USER_REDIS_BACKUP_KEY+"_"+id);
		}

		return retDataList;
	}
	
	
	
	/**
	 * @Description 没使用线程安全查询，但是使用了redis缓存,1000个并发的时候运行一段时间之后还是会挂好几个，虽然减少部分错误.
	 *              在方法加了synchronized虽然能解决部分的并发请求，但是超过1000以上一直卡死不动.完全死锁不释放.
	 * @Author		liangjl
	 * @Date		2018年2月3日 下午2:46:14
	 * @param id
	 * @return 参数
	 * @return List<User> 返回类型 
	 * @throws
	 */
	private /*synchronized*/ List<User> nonUseThreadButRedis(Integer id) {
		Date startDate = new Date() ;//开始时间

		String startDateStr  = DateUtil.formatDate(startDate, "yyyy-MM-dd HH:mm:ss");
		//从缓存获取数据
		List<User> retDataList = (List<User>)redisService.get(USER_REDIS_KEY+"_"+id);
		
		//判断缓存是否等于空
		if(ObjectUtil.isEmpty(retDataList)) {
			//查数据库
			retDataList = userService.getUserList(id);
			Date endDate = new Date() ;//结束时间
			redisService.set(USER_REDIS_KEY+"_"+id, retDataList,20L);
			
			//如果数据库的值为空就把它设置一个过期时间
			if(ObjectUtil.isEmpty(retDataList)) {
				redisService.setExpire(USER_REDIS_KEY+"_"+id, 300L);
			}
			
			String endDateStr= DateUtil.formatDate(endDate, "yyyy-MM-dd HH:mm:ss");
			logger.info("数据库-->执行情况-->>【开始时间为："+startDateStr+"\t,结束时间为："+endDateStr+
					"\t,执行秒数："+DateUtil.getBetweeDay(endDate, startDate)+"】");
			
			return retDataList;
		}else {
			
			Date endDate = new Date() ;//结束时间
			String endDateStr= DateUtil.formatDate(endDate, "yyyy-MM-dd HH:mm:ss");
			logger.info("缓存-->执行情况-->>【开始时间为："+startDateStr+"\t,结束时间为："+endDateStr+
					"\t,执行秒数："+DateUtil.getBetweeDay(endDate, startDate)+"】");
			
			return retDataList;
		}
	}



	/**
	 * @Description 不使用缓存的查询,通过jmeter测试,1000个并发,直接挂很多请求
	 * @Author		liangjl
	 * @Date		2018年2月3日 下午2:34:43
	 * @param id
	 * @return 参数
	 * @return List<User> 返回类型 
	 * @throws
	 */
	private List<User> nonUseRedis(Integer id) {
		
		Date startDate = new Date() ;//开始时间

		String startDateStr  = DateUtil.formatDate(startDate, "yyyy-MM-dd HH:mm:ss");
		List<User> userList = userService.getUserList(id);
		
		Date endDate = new Date() ;//结束时间
		String endDateStr= DateUtil.formatDate(endDate, "yyyy-MM-dd HH:mm:ss");
		
		logger.info("数据库-->执行情况-->>【开始时间为："+startDateStr+"\t,结束时间为："+endDateStr+
				"\t,执行秒数："+DateUtil.getBetweeDay(endDate, startDate)+"】");
		return userList;
	}
	
	
	
	@RequestMapping("/index")
	public ModelAndView index(String param) {
		return new ModelAndView("index");
	}
	
	
}